package com.alexvas.rtsp.widget

import android.content.Context
import android.graphics.SurfaceTexture
import android.net.Uri
import android.util.AttributeSet
import android.util.Log

import android.view.TextureView
import com.alexvas.rtsp.codec.VideoDecodeThread.DecoderType
import com.alexvas.rtsp.codec.VideoDecoderSurfaceThread
import com.alexvas.rtsp.codec.renderer.VideoRenderThread
import com.alexvas.rtsp.widget.RtspProcessor.Statistics
import com.limelight.binding.video.MediaCodecHelper

/**
 * Low latency RTSP stream playback on surface view.
 */
open class RtspTextureView: TextureView {

    private var surfaceWidth = 1920
    private var surfaceHeight = 1080
    private var renderThread: VideoRenderThread? = null
    private var isStart = false
    private var isCreateSurface = false
    private var rtspProcessor = RtspProcessor(
        onVideoDecoderCreateRequested = {
            videoMimeType, videoRotation, videoFrameQueue, videoDecoderListener, videoDecoderType ->
           val surface = renderThread?.codecSurface
            if(surface == null){
                Log.e(TAG, "Codec surface is null")
                return@RtspProcessor null
            }
            VideoDecoderSurfaceThread(
                surface,
                videoMimeType,
                surfaceWidth,
                surfaceHeight,
                videoRotation,
                videoFrameQueue,
                videoDecoderListener,
                videoDecoderType,
            )
        }
    )

    var statistics = Statistics()
        get() = rtspProcessor.statistics
        private set

    var videoRotation: Int
        get() = rtspProcessor.videoRotation
        set(value) { rtspProcessor.videoRotation = value }

    var videoDecoderType: DecoderType
        get() = rtspProcessor.videoDecoderType
        set(value) { rtspProcessor.videoDecoderType = value }

    var experimentalUpdateSpsFrameWithLowLatencyParams: Boolean
        get() = rtspProcessor.experimentalUpdateSpsFrameWithLowLatencyParams
        set(value) { rtspProcessor.experimentalUpdateSpsFrameWithLowLatencyParams = value }

    var debug: Boolean
        get() = rtspProcessor.debug
        set(value) { rtspProcessor.debug = value }

    private val textureListener = object: TextureView.SurfaceTextureListener {
        override fun onSurfaceTextureAvailable(surface: SurfaceTexture, width: Int, height: Int) {
            if (DEBUG) Log.v(TAG, "surfaceCreated()")
            if(renderThread != null){
                return
            }
            renderThread = VideoRenderThread(surface, width, height){
                synchronized( this){
                    isCreateSurface = true
                    startRtspProcessor()
                }

            }.apply { start() }

        }

        override fun onSurfaceTextureSizeChanged(surface: SurfaceTexture, width: Int, height: Int) {
            if (DEBUG) Log.v(TAG, "surfaceChanged( width=$width, height=$height)")
            surfaceWidth = width
            surfaceHeight = height
        }

        override fun onSurfaceTextureDestroyed(surface: SurfaceTexture): Boolean {
            if (DEBUG) Log.v(TAG, "surfaceDestroyed()")
            renderThread?.releaseThread()
            renderThread = null
            synchronized( this){
                isCreateSurface = false
            }
            rtspProcessor.stopDecoders()
            return true
        }

        override fun onSurfaceTextureUpdated(surface: SurfaceTexture) {

        }
    }

    constructor(context: Context) : super(context) {
        initView(context, null, 0)
    }

    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
        initView(context, attrs, 0)
    }

    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
        initView(context, attrs, defStyleAttr)
    }

    private fun initView(context: Context, attrs: AttributeSet?, defStyleAttr: Int) {
        if (DEBUG) Log.v(TAG, "initView()")
        MediaCodecHelper.initialize(context, /*glRenderer*/ "")
        surfaceTextureListener = textureListener

    }

    fun init(uri: Uri, username: String?, password: String?, userAgent: String?) {
        if (DEBUG) Log.v(TAG, "init(uri='$uri', username='$username', password='$password', userAgent='$userAgent')")
        rtspProcessor.init(uri, username, password, userAgent)
    }

    private var requestVideo: Boolean = false
    private var requestAudio: Boolean = false
    private var requestApplication: Boolean = false

    /**
     * Start RTSP client.
     *
     * @param requestVideo request video track
     * @param requestAudio request audio track
     * @param requestApplication request application track
     * @see https://datatracker.ietf.org/doc/html/rfc4566#section-5.14
     */
    fun start(requestVideo: Boolean, requestAudio: Boolean, requestApplication: Boolean = false) {
        if (DEBUG) Log.v(TAG, "start(requestVideo=$requestVideo, requestAudio=$requestAudio, requestApplication=$requestApplication)")

        this.requestVideo = requestVideo
        this.requestAudio = requestAudio
        this.requestApplication = requestApplication
        synchronized(this){
            isStart = true
            startRtspProcessor()
        }

    }
    private fun startRtspProcessor(){
        if(isStart && isCreateSurface){
            rtspProcessor.start(requestVideo, requestAudio, requestApplication)
        }
    }

    /**
     * Stop RTSP client.
     */
    fun stop() {
        synchronized(this){
            isStart = false
        }
        if (DEBUG) Log.v(TAG, "stop()")
        rtspProcessor.stop()
        rtspProcessor.stopDecoders()
    }

    fun isStarted(): Boolean {
        return rtspProcessor.isStarted()
    }

    fun setStatusListener(listener: RtspStatusListener?) {
        if (DEBUG) Log.v(TAG, "setStatusListener()")
        rtspProcessor.statusListener = listener
    }

    fun setDataListener(listener: RtspDataListener?) {
        if (DEBUG) Log.v(TAG, "setDataListener()")
        rtspProcessor.dataListener = listener
    }

    companion object {
        private val TAG: String = RtspTextureView::class.java.simpleName
        private const val DEBUG = false
    }

}



